home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Manuels & Misc / Assembly / AOA.ZIP / CH19 / SHMALLOC.ASM < prev    next >
Encoding:
Assembly Source File  |  1994-07-19  |  20.5 KB  |  819 lines

  1. ; SHMALLOC.ASM
  2. ;
  3. ; This TSR sets up a dynamic shared memory system.
  4. ;
  5. ; This TSR checks to make sure there isn't a copy already active in
  6. ; memory.  When removing itself from memory, it makes sure there are
  7. ; no other interrupts chained into INT 2Fh before doing the remove.
  8. ;
  9. ;
  10. ;
  11. ; The following segments must appear in this order and before the
  12. ; Standard Library includes.
  13.  
  14. ResidentSeg    segment    para public 'Resident'
  15. ResidentSeg    ends
  16.  
  17. SharedMemory    segment    para public 'Shared'
  18. SharedMemory    ends
  19.  
  20. EndResident    segment    para public 'EndRes'
  21. EndResident    ends
  22.  
  23.         .xlist
  24.         .286
  25.         include     stdlib.a
  26.         includelib    stdlib.lib
  27.         .list
  28.  
  29.  
  30. ; Resident segment that holds the TSR code:
  31.  
  32. ResidentSeg    segment    para public 'Resident'
  33.         assume    cs:ResidentSeg, ds:nothing
  34.  
  35.  
  36. NULL        equ    0
  37.  
  38.  
  39. ; Data structure for an allocated data region.
  40. ;
  41. ; Key-    user supplied ID to associate this region with a particular set
  42. ;    of processes.
  43. ;
  44. ; Next-    Points at the next allocated block.
  45. ; Prev- Points at the previous allocated block.
  46. ; Size- Size (in bytes) of allocated block, not including header structure.
  47.  
  48. Region        struct
  49. key        word    ?
  50. next        word    ?
  51. prev        word    ?
  52. blksize        word    ?
  53. Region        ends
  54.  
  55. Startmem    equ    Region ptr [0]
  56.  
  57. AllocatedList    word    0        ;Points at chain of alloc'd blocks.
  58. FreeList    word    0        ;Points at chain of free blocks.
  59.  
  60. ; Int 2Fh ID number for this TSR:
  61.  
  62. MyTSRID        byte    0
  63.         byte    0        ;Padding so we can print it.
  64.  
  65. ; PSP is the psp address for this program.
  66.  
  67. PSP        word    0
  68.  
  69. OldInt2F    dword    ?
  70.  
  71.  
  72. ; MyInt2F-    Provides int 2Fh (multiplex interrupt) support for this
  73. ;        TSR.  The multiplex interrupt recognizes the following
  74. ;        subfunctions (passed in AL):
  75. ;
  76. ;        00h- Verify presence.      Returns 0FFh in AL and a pointer
  77. ;                    to an ID string in es:di if the
  78. ;                    TSR ID (in AH) matches this
  79. ;                    particular TSR.
  80. ;
  81. ;        01h- Remove.        Removes the TSR from memory.
  82. ;                    Returns 0 in AL if successful,
  83. ;                    1 in AL if failure.
  84. ;
  85. ;        11h- shmalloc        CX contains the size of the block
  86. ;                       to allocate.
  87. ;                    DX contains the key for this block.
  88. ;                    Returns a pointer to block in ES:DI
  89. ;                     and size of allocated block in CX.
  90. ;                    Returns an error code in AX.  Zero
  91. ;                     is no error, one is "key already
  92. ;                     exists,"  two is "insufficient
  93. ;                     memory for request."
  94. ;
  95. ;        12h- shmfree        DX contains the key for this block.
  96. ;                    This call frees the specified block
  97. ;                     from memory.
  98. ;
  99. ;        13h- shminit        Initializes the shared memory system
  100. ;                     freeing all blocks currently in
  101. ;                     use.
  102. ;
  103. ;        14h- shmattach        DX contains the key for a block.
  104. ;                     Search for that block and return
  105. ;                     its address in ES:DI.  AX contains
  106. ;                     zero if successful, three if it
  107. ;                     cannot locate a block with the
  108. ;                     specified key.
  109.  
  110. MyInt2F        proc    far
  111.         assume    ds:nothing
  112.  
  113.         cmp    ah, MyTSRID    ;Match our TSR identifier?
  114.         je    YepItsOurs
  115.         jmp    OldInt2F
  116.  
  117. ; Okay, we know this is our ID, now check for a verify, remove, or
  118. ; return segment call.
  119.  
  120. YepItsOurs:    cmp    al, 0        ;Verify Call
  121.         jne    TryRmv
  122.         mov    al, 0ffh    ;Return success.
  123.         lesi    IDString
  124.         iret            ;Return back to caller.
  125.  
  126. IDString    byte    "Dynamic Shared Memory TSR",0
  127.  
  128. TryRmv:        cmp    al, 1        ;Remove call.
  129.         jne    Tryshmalloc
  130.  
  131. ; See if we can remove this TSR:
  132.  
  133.         push    es
  134.         mov    ax, 0
  135.         mov    es, ax
  136.         cmp    word ptr es:[2Fh*4], offset MyInt2F
  137.         jne    TRDone
  138.         cmp    word ptr es:[2Fh*4 + 2], seg MyInt2F
  139.         je    CanRemove    ;Branch if we can.
  140. TRDone:        mov    ax, 1        ;Return failure for now.
  141.         pop    es
  142.         iret
  143.  
  144. ; Okay, they want to remove this guy *and* we can remove it from memory.
  145. ; Take care of all that here.
  146.  
  147.         assume    ds:ResidentSeg
  148.  
  149. CanRemove:    push    ds
  150.         pusha
  151.         cli            ;Turn off the interrupts while
  152.         mov    ax, 0        ; we mess with the interrupt
  153.         mov    es, ax        ; vectors.
  154.         mov    ax, cs
  155.         mov    ds, ax
  156.  
  157.         mov    ax, word ptr OldInt2F
  158.         mov    es:[2Fh*4], ax
  159.         mov    ax, word ptr OldInt2F+2
  160.         mov    es:[2Fh*4 + 2], ax
  161.  
  162.  
  163. ; Okay, one last thing before we quit- Let's give the memory allocated
  164. ; to this TSR back to DOS.
  165.  
  166.         mov    ds, PSP
  167.         mov    es, ds:[2Ch]        ;Ptr to environment block.
  168.         mov    ah, 49h            ;DOS release memory call.
  169.         int    21h
  170.  
  171.         mov    ax, ds            ;Release program code space.
  172.         mov    es, ax
  173.         mov    ah, 49h
  174.         int    21h
  175.  
  176.         popa
  177.         pop    ds
  178.         pop    es
  179.         mov    ax, 0            ;Return Success.
  180.         iret
  181.  
  182.  
  183.  
  184. ; Stick BadKey here so that it is close to its associated branch (from below).
  185. ;
  186. ; If come here, we've discovered an allocated block with the
  187. ; specified key.  Return an error code (AX=1) and the size of that
  188. ; allocated block (in CX).
  189.  
  190. BadKey:        mov    cx, [bx].Region.BlkSize
  191.         mov    ax, 1            ;Already allocated error.
  192.         pop    bx
  193.         pop    ds
  194.         iret
  195.  
  196.  
  197. ; See if this is a shmalloc call.
  198. ; If so, on entry -
  199. ; DX contains the key.
  200. ; CX contains the number of bytes to allocate.
  201. ;
  202. ; On exit:
  203. ;
  204. ; ES:DI points at the allocated block (if successful).
  205. ; CX contains the actual size of the allocated block (>=CX on entry).
  206. ; AX contains error code, 0 if no error.
  207.  
  208. Tryshmalloc:    cmp    al, 11h            ;shmalloc function code.
  209.         jne     Tryshmfree
  210.  
  211. ; First, search through the allocated list to see if a block with the
  212. ; current key number already exists.  DX contains the requested key.
  213.  
  214.         assume    ds:SharedMemory
  215.         assume    bx:ptr Region
  216.         assume    di:ptr Region
  217.  
  218.         push    ds
  219.         push    bx
  220.         mov    bx, SharedMemory
  221.         mov    ds, bx
  222.  
  223.         mov    bx, ResidentSeg:AllocatedList
  224.         test    bx, bx            ;Anything on this list?
  225.         je    SrchFreeList
  226.  
  227. SearchLoop:    cmp    dx, [bx].Key        ;Key exist already?
  228.         je    BadKey
  229.         mov    bx, [bx].Next        ;Get next region.
  230.         test    bx, bx            ;NULL?, if not, try another
  231.         jne    SearchLoop        ; entry in the list.
  232.  
  233. ; If an allocated block with the specified key does not already exist,
  234. ; then try to allocate one from the free memory list.
  235.  
  236. SrchFreeList:    mov    bx, ResidentSeg:FreeList
  237.         test    bx, bx            ;Empty free list?
  238.         je    OutaMemory
  239.  
  240. FirstFitLp:    cmp    cx, [bx].BlkSize    ;Is this block big enough?
  241.         jbe    GotBlock
  242.         mov    bx, [bx].Next        ;If not, on to the next one.
  243.         test    bx, bx            ;Anything on this list?
  244.         jne    FirstFitLp
  245.  
  246. ; If we drop down here, we were unable to find a block that was large
  247. ; enough to satisfy the request.  Return an appropriate error
  248.  
  249. OutaMemory:    mov    cx, 0            ;Nothing available.
  250.         mov    ax, 2            ;Insufficient memory error.
  251.         pop    bx
  252.         pop    ds
  253.         iret
  254.  
  255. ; If we find a large enough block, we've got to carve the new block
  256. ; out of it and return the rest of the storage to the free list.  If the
  257. ; free block is at least 32 bytes larger than the requested size, we will
  258. ; do this.  If the free block is less than 32 bytes larger, we will simply
  259. ; give this free block to the requesting process.  The reason for the
  260. ; 32 bytes is simple:  We need eight bytes for the new block's header
  261. ; (the free block already has one) and it doesn't make sense to fragment
  262. ; blocks to sizes below 24 bytes.  That would only increase processing time
  263. ; when processes free up blocks by requiring more work coalescing blocks.
  264.  
  265. GotBlock:    mov    ax, [bx].BlkSize    ;Compute difference in size.
  266.         sub    ax, cx
  267.         cmp    ax, 32            ;At least 32 bytes left?
  268.         jbe    GrabWholeBlk        ;If not, take this block.
  269.  
  270. ; Okay, the free block is larger than the requested size by more than 32
  271. ; bytes.  Carve the new block from the end of the free block  (that way
  272. ; we do not have to change the free block's pointers, only the size.
  273.  
  274.         mov    di, bx
  275.         add    di, [bx].BlkSize    ;Scoot to end, minus 8
  276.         sub    di, cx            ;Point at new block.
  277.  
  278.         sub    [bx].BlkSize, cx    ;Remove alloc'd block and
  279.         sub    [bx].BlkSize, 8        ; room for header.
  280.  
  281.         mov    [di].BlkSize, cx    ;Save size of block.
  282.         mov    [di].Key, dx        ;Save key.
  283.  
  284. ; Link the new block into the list of allocated blocks.
  285.  
  286.         mov    bx, ResidentSeg:AllocatedList
  287.         mov    [di].Next, bx
  288.         mov    [di].Prev, NULL        ;NULL previous pointer.
  289.         test    bx, bx            ;See if it was an empty list.
  290.         je    NoPrev
  291.         mov    [bx].Prev, di        ;Set prev ptr for old guy.
  292.  
  293. NoPrev:        mov    ResidentSeg:AllocatedList, di
  294. RmvDone:    add    di, 8            ;Point at actual data area.
  295.         mov    ax, ds            ;Return ptr in es:di.
  296.         mov    es, ax
  297.         mov    ax, 0            ;Return success.
  298.         pop    bx
  299.         pop    ds
  300.         iret
  301.  
  302.  
  303. ; If the current free block is larger than the request, but not by more
  304. ; that 32 bytes, just give the whole block to the user.
  305.  
  306. GrabWholeBlk:   mov    di, bx
  307.         mov    cx, [bx].BlkSize    ;Return actual size.
  308.         cmp    [bx].Prev, NULL        ;First guy in list?
  309.         je    Rmv1st
  310.         cmp    [bx].Next, NULL        ;Last guy in list?
  311.         je    RmvLast
  312.  
  313. ; Okay, this record is sandwiched between two other in the free list.
  314. ; Cut it out from among the two.
  315.  
  316.         mov    ax, [bx].Next        ;Save the ptr to the next
  317.         mov    bx, [bx].Prev        ; item in the prev item's
  318.         mov    [bx].Next, ax        ; next field.
  319.  
  320.         mov    ax, bx            ;Save the ptr to the prev
  321.         mov    bx, [di].Next        ; item in the next item's
  322.         mov    [bx].Prev, bx        ; prev field.
  323.         jmp    RmvDone
  324.  
  325.  
  326.  
  327. ; The block we want to remove is at the beginning of the free list.
  328. ; It could also be the only item on the free list!
  329.  
  330. Rmv1st:         mov    ax, [bx].Next
  331.         mov    FreeList, ax        ;Remove from free list.
  332.         jmp    RmvDone
  333.  
  334. ; If the block we want to remove is at the end of the list, handle that
  335. ; down here.
  336.  
  337. RmvLast:    mov    bx, [bx].Prev
  338.         mov    [bx].Next, NULL
  339.         jmp    RmvDone
  340.  
  341.         assume    ds:nothing, bx:nothing, di:nothing
  342.  
  343.  
  344.  
  345.  
  346. ; This code handles the SHMFREE function.
  347. ; On entry, DX contains the key for the block to free.  We need to
  348. ; search through the allocated block list and find the block with that
  349. ; key.  If we do not find such a block, this code returns without doing
  350. ; anything.  If we find the block, we need to add its memory to the
  351. ; free pool.  However, we cannot simply insert this block on the front
  352. ; of the free list (as we did for the allocated blocks).  It might
  353. ; turn out that this block we're freeing is adjacent to one or two
  354. ; other free blocks.  This code has to coalesce such blocks into
  355. ; a single free block.
  356.  
  357. Tryshmfree:    cmp    al, 12h
  358.         jne    Tryshminit
  359.  
  360.  
  361. ; First, search the allocated block list to see if we can find the
  362. ; block to remove.  If we don't find it in the list anywhere, just return.
  363.  
  364.         assume    ds:SharedMemory
  365.         assume    bx:ptr Region
  366.         assume    di:ptr Region
  367.  
  368.         push    ds
  369.         push    di
  370.         push    bx
  371.  
  372.         mov    bx, SharedMemory
  373.         mov    ds, bx
  374.         mov    bx, ResidentSeg:AllocatedList
  375.  
  376.         test    bx, bx            ;Empty allocated list?
  377.         je    FreeDone
  378. SrchList:    cmp    dx, [bx].Key        ;Search for key in DX.
  379.         je    FoundIt
  380.         mov    bx, [bx].Next
  381.         test    bx, bx            ;At end of list?
  382.         jne    SrchList
  383. FreeDone:    pop    bx
  384.         pop    di            ;Nothing allocated, just
  385.         pop    ds            ; return to caller.
  386.         iret
  387.  
  388.  
  389. ; Okay, we found the block the user wants to delete.  Remove it from
  390. ; the allocated list.  There are three cases to consider:
  391. ; (1) it is at the front of the allocated list, (2) it is at the end of
  392. ; the allocated list, and (3) it is in the middle of the allocated list.
  393.  
  394. FoundIt:    cmp    [bx].Prev, NULL        ;1st item in list?
  395.         je    Free1st
  396.         cmp    [bx].Next, NULL        ;Last item in list?
  397.         je    FreeLast
  398.  
  399. ; Okay, we're removing an allocated item from the middle of the allocated
  400. ; list.
  401.  
  402.         mov    di, [bx].Next        ;[next].prev := [cur].prev
  403.         mov    ax, [bx].Prev
  404.         mov    [di].Prev, ax
  405.         xchg    ax, di
  406.         mov    [di].Next, ax        ;[prev].next := [cur].next
  407.         jmp    AddFree
  408.  
  409. ; Handle the case where we are removing the first item from the allocation
  410. ; list.  It is possible that this is the only item on the list (i.e., it
  411. ; is the first and last item), but this code handles that case without any
  412. ; problems.
  413.  
  414. Free1st:    mov    ax, [bx].Next
  415.         mov    ResidentSeg:AllocatedList, ax
  416.         jmp    AddFree
  417.  
  418. ; If we're removing the last guy in the chain, simply set the next field
  419. ; of the previous node in the list to NULL.
  420.  
  421. FreeLast:    mov    di, [bx].Prev
  422.         mov    [di].Next, NULL
  423.  
  424. ; Okay, now we've got to put the freed block onto the free block list.
  425. ; The free block list is sorted according to address.  We have to search
  426. ; for the first free block whose address is greater than the block we've
  427. ; just freed and insert the new free block before that one.  If the two
  428. ; blocks are adjacent, then we've got to merge them into a single free
  429. ; block.  Also, if the block before is adjacent, we must merge it as
  430. ; well.  This will coalesce all free blocks on the free list so there
  431. ; are as few free blocks as possible and those blocks are as large as
  432. ; possible.
  433.  
  434. AddFree:    mov    ax, ResidentSeg:FreeList
  435.         test    ax, ax            ;Empty list?
  436.         jne    SrchPosn
  437.  
  438. ; If the list is empty, stick this guy on as the only entry.
  439.  
  440.         mov    ResidentSeg:FreeList, bx
  441.         mov    [bx].Next, NULL
  442.         mov    [bx].Prev, NULL
  443.         jmp    FreeDone
  444.  
  445. ; If the free list is not empty, search for the position of this block
  446. ; in the free list:
  447.  
  448. SrchPosn:    mov    di, ax
  449.         cmp    bx, di
  450.         jb    FoundPosn
  451.         mov    ax, [di].Next
  452.         test    ax, ax            ;At end of list?
  453.         jne    SrchPosn
  454.  
  455. ; If we fall down here, the free block belongs at the end of the list.
  456. ; See if we need to merge the new block with the old one.
  457.  
  458.         mov    ax, di
  459.         add    ax, [di].BlkSize    ;Compute address of 1st byte
  460.         add    ax, 8            ; after this block.
  461.         cmp    ax, bx
  462.         je    MergeLast
  463.  
  464. ; Okay, just add the free block to the end of the list.
  465.  
  466.         mov    [di].Next, bx
  467.         mov    [bx].Prev, di
  468.         mov    [bx].Next, NULL
  469.         jmp    FreeDone
  470.  
  471. ; Merge the freed block with the block DI points at.
  472.  
  473. MergeLast:    mov    ax, [di].BlkSize
  474.         add    ax, [bx].BlkSize
  475.         add    ax, 8
  476.         mov    [di].BlkSize, ax
  477.         jmp    FreeDone
  478.  
  479. ; If we found a free block before which we are supposed to insert
  480. ; the current free block, drop down here and handle it.
  481.  
  482. FoundPosn:      mov    ax, bx            ;Compute the address of the
  483.         add    ax, [bx].BlkSize    ; next block in memory.
  484.         add    ax, 8
  485.         cmp    ax, di            ;Equal to this block?
  486.         jne    DontMerge
  487.  
  488. ; The next free block is adjacent to the one we're freeing, so just
  489. ; merge the two.
  490.  
  491.         mov    ax, [di].BlkSize    ;Merge the sizes together.
  492.         add    ax, 8
  493.         add    [bx].BlkSize, ax
  494.         mov    ax, [di].Next        ;Tweak the links.
  495.         mov    [bx].Next, ax
  496.         mov    ax, [di].Prev
  497.         mov    [bx].Prev, ax
  498.         jmp    TryMergeB4
  499.  
  500. ; If the blocks are not adjacent, just link them together here.
  501.  
  502. DontMerge:    mov    ax, [di].Prev
  503.         mov    [di].Prev, bx
  504.         mov    [bx].Prev, ax
  505.         mov    [bx].Next, di
  506.  
  507. ; Now, see if we can merge the current free block with the previous free blk.
  508.  
  509. TryMergeB4:    mov    di, [bx].Prev
  510.         mov    ax, di
  511.         add    ax, [di].BlkSize
  512.         add    ax, 8
  513.         cmp    ax, bx
  514.         je    CanMerge
  515.         pop    bx
  516.         pop    di            ;Nothing allocated, just
  517.         pop    ds            ; return to caller.
  518.         iret
  519.  
  520. ; If we can merge the previous and current free blocks, do that here:
  521.  
  522. CanMerge:    mov    ax, [bx].Next
  523.         mov    [di].Next, ax
  524.         mov    ax, [bx].BlkSize
  525.         add    ax, 8
  526.         add    [di].BlkSize, ax
  527.         pop    bx
  528.         pop    di
  529.         pop    ds
  530.         iret
  531.  
  532.         assume    ds:nothing
  533.         assume    bx:nothing
  534.         assume    di:nothing
  535.  
  536. ; Here's where we handle the shared memory initializatin (SHMINIT) function.
  537. ; All we got to do is create a single block on the free list (which is all
  538. ; available memory), empty out the allocated list, and then zero out all
  539. ; shared memory.
  540.  
  541. Tryshminit:    cmp    al, 13h
  542.         jne    TryShmAttach
  543.  
  544. ; Reset the memory allocation area to contain a single, free, block of
  545. ; memory whose size is 0FFF8h (need to reserve eight bytes for the block's
  546. ; data structure).
  547.  
  548.         push    es
  549.         push    di
  550.         push    cx
  551.  
  552.         mov    ax, SharedMemory    ;Zero out the shared
  553.         mov    es, ax            ; memory segment.
  554.         mov    cx, 32768
  555.         xor    ax, ax
  556.         mov    di, ax
  557.     rep    stosw
  558.  
  559.  
  560. ; Note: the commented out lines below are unnecessary since the code above
  561. ; has already zeroed out the entire shared memory segment.
  562. ; Note: we cannot put the first record at offset zero because offset zero
  563. ; is the special value for the NULL pointer.  We'll use 4 instead.
  564.  
  565.         mov    di, 4
  566. ;        mov    es:[di].Region.Key, 0        ;Key is arbitrary.
  567. ;        mov    es:[di].Region.Next, 0        ;No other entries.
  568. ;        mov    es:[di].Region.Prev, 0        ; Ditto.
  569.         mov    es:[di].Region.BlkSize, 0FFF8h    ;Rest of segment.
  570.         mov    ResidentSeg:FreeList, di
  571.  
  572.         pop    cx
  573.         pop    di
  574.         pop    es
  575.         mov    ax, 0                ;Return no error.
  576.         iret
  577.  
  578.  
  579. ; Handle the SHMATTACH function here.  On entry, DX contains a key number.
  580. ; Search for an allocated block with that key number and return a pointer
  581. ; to that block (if found) in ES:DI.  Return an error code (AX=3) if we
  582. ; cannot find the block.
  583.  
  584. TryShmAttach:    cmp    al, 14h            ;Attach opcode.
  585.         jne    IllegalOp
  586.         mov    ax, SharedMemory
  587.         mov    es, ax
  588.  
  589.         mov    di, ResidentSeg:AllocatedList
  590. FindOurs:    cmp    dx, es:[di].Region.Key
  591.         je    FoundOurs
  592.         mov    di, es:[di].Region.Next
  593.         test    di, di
  594.         jne    FoundOurs
  595.         mov    ax, 3            ;Can't find the key.
  596.         iret
  597.  
  598. FoundOurs:    add    di, 8            ;Point at actual data.
  599.         mov    ax, 0            ;No error.
  600.         iret
  601.  
  602.  
  603. ; They called us with an illegal subfunction value.  Try to do as little
  604. ; damage as possible.
  605.  
  606. IllegalOp:    mov    ax, 0        ;Who knows what they were thinking?
  607.         iret
  608. MyInt2F        endp
  609.         assume    ds:nothing
  610. ResidentSeg    ends
  611.  
  612.  
  613. ; Here's the segment that will actually hold the shared data.
  614.  
  615. SharedMemory    segment    para public 'Shared'
  616.         db    0FFFFh dup (?)
  617. SharedMemory    ends
  618.  
  619.  
  620.  
  621.  
  622.  
  623.  
  624. cseg        segment    para public 'code'
  625.         assume    cs:cseg, ds:ResidentSeg
  626.  
  627. ; SeeIfPresent-    Checks to see if our TSR is already present in memory.
  628. ;        Sets the zero flag if it is, clears the zero flag if
  629. ;        it is not.
  630.  
  631. SeeIfPresent    proc    near
  632.         push    es
  633.         push    ds
  634.         push    di
  635.         mov    cx, 0ffh        ;Start with ID 0FFh.
  636. IDLoop:        mov    ah, cl
  637.         push    cx
  638.         mov    al, 0            ;Verify presence call.
  639.         int    2Fh
  640.         pop    cx
  641.         cmp    al, 0            ;Present in memory?
  642.         je    TryNext
  643.         strcmpl
  644.         byte    "Dynamic Shared Memory TSR",0
  645.         je    Success
  646.  
  647. TryNext:    dec    cl            ;Test USER IDs of 80h..FFh
  648.         js    IDLoop
  649.         cmp    cx, 0            ;Clear zero flag.
  650. Success:    pop    di
  651.         pop    ds
  652.         pop    es
  653.         ret
  654. SeeIfPresent    endp
  655.  
  656.  
  657.  
  658. ; FindID-    Determines the first (well, last actually) TSR ID available
  659. ;        in the multiplex interrupt chain.  Returns this value in
  660. ;        the CL register.
  661. ;
  662. ;        Returns the zero flag set if it locates an empty slot.
  663. ;        Returns the zero flag clear if failure.
  664.  
  665. FindID        proc    near
  666.         push    es
  667.         push    ds
  668.         push    di
  669.  
  670.         mov    cx, 0ffh        ;Start with ID 0FFh.
  671. IDLoop:        mov    ah, cl
  672.         push    cx
  673.         mov    al, 0            ;Verify presence call.
  674.         int    2Fh
  675.         pop    cx
  676.         cmp    al, 0            ;Present in memory?
  677.         je    Success
  678.         dec    cl            ;Test USER IDs of 80h..FFh
  679.         js    IDLoop
  680.         xor    cx, cx
  681.         cmp    cx, 1            ;Clear zero flag
  682. Success:    pop    di
  683.         pop    ds
  684.         pop    es
  685.         ret
  686. FindID        endp
  687.  
  688.  
  689.  
  690. Main        proc
  691.         meminit
  692.  
  693.         mov    ax, ResidentSeg
  694.         mov    ds, ax
  695.  
  696.         mov    ah, 62h            ;Get this program's PSP
  697.         int    21h            ; value.
  698.         mov    PSP, bx
  699.  
  700. ; Before we do anything else, we need to check the command line
  701. ; parameters.  If there is one, and it is the word "REMOVE", then remove
  702. ; the resident copy from memory using the multiplex (2Fh) interrupt.
  703.  
  704.         argc
  705.         cmp    cx, 1            ;Must have 0 or 1 parms.
  706.         jb    TstPresent
  707.         je    DoRemove
  708. Usage:        print
  709.         byte    "Usage:",cr,lf
  710.         byte    "       shmalloc",cr,lf
  711.         byte    "or     shmalloc REMOVE",cr,lf,0
  712.         ExitPgm
  713.  
  714.  
  715. ; Check for the REMOVE command.
  716.  
  717. DoRemove:    mov    ax, 1
  718.         argv
  719.         stricmpl
  720.         byte    "REMOVE",0
  721.         jne    Usage
  722.  
  723.         call    SeeIfPresent
  724.         je    RemoveIt
  725.         print
  726.         byte    "TSR is not present in memory, cannot remove"
  727.         byte    cr,lf,0
  728.         ExitPgm
  729.  
  730. RemoveIt:    mov    MyTSRID, cl
  731.         printf
  732.         byte    "Removing TSR (ID #%d) from memory...",0
  733.         dword    MyTSRID
  734.  
  735.         mov    ah, cl
  736.         mov    al, 1            ;Remove cmd, ah contains ID
  737.         int    2Fh
  738.         cmp    al, 1            ;Succeed?
  739.         je    RmvFailure
  740.         print
  741.         byte    "removed.",cr,lf,0
  742.         ExitPgm
  743.  
  744. RmvFailure:    print
  745.         byte    cr,lf
  746.         byte    "Could not remove TSR from memory.",cr,lf
  747.         byte    "Try removing other TSRs in the reverse order "
  748.         byte    "you installed them.",cr,lf,0
  749.         ExitPgm
  750.  
  751.  
  752.  
  753. ; Okay, see if the TSR is already in memory.  If so, abort the
  754. ; installation process.
  755.  
  756. TstPresent:     call    SeeIfPresent
  757.         jne    GetTSRID
  758.         print
  759.         byte    "TSR is already present in memory.",cr,lf
  760.         byte    "Aborting installation process",cr,lf,0
  761.         ExitPgm
  762.  
  763.  
  764. ; Get an ID for our TSR and save it away.
  765.  
  766. GetTSRID:    call    FindID
  767.         je    GetFileName
  768.         print
  769.         byte    "Too many resident TSRs, cannot install",cr,lf,0
  770.         ExitPgm
  771.  
  772.  
  773. ; Things look cool so far, so install the interrupts
  774.  
  775. GetFileName:    mov    MyTSRID, cl
  776.         print
  777.         byte    "Installing interrupts...",0
  778.  
  779.  
  780. ; Patch into the INT 2Fh interrupt chain.
  781.  
  782.         cli                ;Turn off interrupts!
  783.         mov    ax, 0
  784.         mov    es, ax
  785.         mov    ax, es:[2Fh*4]
  786.         mov    word ptr OldInt2F, ax
  787.         mov     ax, es:[2Fh*4 + 2]
  788.         mov    word ptr OldInt2F+2, ax
  789.         mov    es:[2Fh*4], offset MyInt2F
  790.         mov    es:[2Fh*4+2], seg ResidentSeg
  791.         sti                ;Okay, ints back on.
  792.  
  793. ; We're hooked up, the only thing that remains is to initialize the shared
  794. ; memory segment and then terminate and stay resident.
  795.  
  796.         printf
  797.         byte    "Installed, TSR ID #%d.",cr,lf,0
  798.         dword    MyTSRID
  799.  
  800.         mov    ah, MyTSRID        ;Initialization call.
  801.         mov    al, 13h
  802.         int    2Fh
  803.  
  804.         mov    dx, EndResident        ;Compute size of program.
  805.         sub    dx, PSP
  806.         mov    ax, 3100h        ;DOS TSR command.
  807.         int    21h
  808. Main        endp
  809. cseg        ends
  810.  
  811. sseg        segment    para stack 'stack'
  812. stk        db    256 dup (?)
  813. sseg        ends
  814.  
  815. zzzzzzseg    segment    para public 'zzzzzz'
  816. LastBytes    db    16 dup (?)
  817. zzzzzzseg    ends
  818.         end    Main
  819.